"""Make pip and Homebrew play nicely together.

I want pip to play nicely with Homebrew. This gives me pip's dependency support
when installing Python-based apps while Homebrew handles the overall management
of the app, keeping everything in one place.

Ideally this would all be a part of Homebrew as a base formula for managing
Python apps, giving me a single app for installing applications without
having to remember what language some tool was written in.

"""
import os
import shutil
import stat
import subprocess
import sys
import xmlrpclib


class App(object):

    """Represent an application to be under the management of Homebrew but
    installed by pip."""

    def __init__(self, name):
        self.name = name
        brew = subprocess.Popen('brew --cellar', shell=True,
                                stdout=subprocess.PIPE)
        cellar = brew.communicate()[0].strip()
        self._cellar_target = os.path.join(cellar, name)
        self.installed = self._installed_version()
        self.newest, self.pypi_name = self._newest_version()
        # TODO(distutils2) version objects for proper version comparison
        self.outdated = self.installed < self.newest

    def _installed_version(self):
        """Return the version of the installed app.

        If the app is not installed then return an empty string.

        """
        # TODO(homebrew) not needed
        if not os.path.exists(self._cellar_target):
            return ""
        versions = os.listdir(self._cellar_target)
        if not versions:
            return ""
        else:
            versions.sort()
            return versions[-1]

    def _newest_version(self):
        """Find out the newest version available on PyPI."""
        # TODO(homebrew) not needed
        pypi = xmlrpclib.ServerProxy('http://pypi.python.org/pypi')
        search = pypi.search({'name': self.name.split()})
        for found in search:
            if found['name'].lower() == self.name:
                pypi_name = found['name']
                break
        else:
            raise ValueError("could not find PyPI name for %s" % self.name)
        versions = pypi.package_releases(pypi_name)
        versions.sort()
        return versions[-1], pypi_name


    def deactivate(self):
        """Deactivate the application through Homebrew."""
        # TODO(homebrew) not needed
        subprocess.check_call("brew unlink %s" % self.name, shell=True)

    def uninstall(self):
        "Uninstall the installed version of the application."""
        shutil.rmtree(os.path.join(self._cellar_target, self.installed))


    def install(self):
        """Install the newest version of the app as found on PyPI."""
        # Install into the Cellar.
        if self.installed:
            print "Deactivating %s" % self.installed
            self.deactivate()
        cellar_ver = os.path.join(self._cellar_target, self.newest)
        created_app_dir = not os.path.exists(self._cellar_target)
        os.makedirs(cellar_ver)
        try:
            print "Installing to %s" % cellar_ver
            print
            app_bin = os.path.join(cellar_ver, 'bin')
            app_lib = os.path.join(cellar_ver, 'lib', self.name)
            cmd = ('pip install '
                  '--install-option="--install-lib=%s" '
                  '--install-option="--install-scripts=%s" '
                  '%s') % (app_lib, app_bin, self.pypi_name)
            print cmd
            subprocess.check_call(cmd, shell=True)
            # Move executable to be __main__.py.
            # XXX check that __main__.py doesn't already exist
            print "Renaming executable"
            executables = os.listdir(app_bin)
            # XXX prompt the user if there is more than one executable
            assert len(executables) == 1
            executable = os.path.join(app_bin, executables[0])
            os.rename(executable, os.path.join(app_lib, '__main__.py'))
            # Replace the old executable with a new shell script.
            print "Creating shell script"
            with open(executable, 'w') as file:
                file.write('#!/bin/sh\n')
                file.write('%s %s "$@"\n' % (sys.executable, app_lib))
            os.chmod(executable, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR |
                                 stat.S_IRGRP | stat.S_IXGRP |
                                 stat.S_IROTH | stat.S_IXOTH)
            # Uninstall the old version if needed (Homebrew only wants one).
            # TODO(homebrew) not needed
            if self.installed:
                print "Uninstalling %s" % self.installed
                self.uninstall()
        except:
            shutil.rmtree(cellar_ver)
            if created_app_dir:
                os.rmdir(self._cellar_target)
            raise
        # Link the app through Homebrew.
        # TODO(homebrew) not needed
        print "Activating %s" % self.newest
        subprocess.check_call("brew link %s" % self.name, shell=True)


def main(args=sys.argv[1:]):
    # XXX CLI
    app = App(args[0])
    installed_str = "Installed:"
    print "Installed:", app.installed
    print "Newest:".ljust(len(installed_str)), app.newest,
    if app.name != app.pypi_name:
        print "[%s]" % app.pypi_name
    else:
        print ""
    print
    if not app.outdated:
        print "Nothing to do."
        return
    app.install()



if __name__ == '__main__':
    main()
